package clickhouse

import (
	"fmt"
	"strings"
	"time"

	"gorm.io/driver/clickhouse"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
	"gorm.io/gorm/schema"

	"gitee.com/csingo/cDB/logger"

	"gitee.com/csingo/cLog"

	"gitee.com/csingo/cDB/drivers"
)

type Connector struct {
	connection string
	config     *Connection
	db         *gorm.DB

	drivers.GormQuery
}

func (i *Connector) GetConnection() string {
	return i.connection
}

func (i *Connector) GetDriver() drivers.DriverInterface {
	return i.config
}

func (i *Connector) GetDB() any {
	return i.db
}

func (i *Connector) Connect() error {
	var err error
	var dsn strings.Builder

	if i.config == nil {
		return fmt.Errorf("click database config is nil")
	}

	// clickhouse://gorm:gorm@localhost:9942/gorm?dial_timeout=10s&read_timeout=20s
	dsn.WriteString("clickhouse://")
	dsn.WriteString(i.config.Username)
	dsn.WriteString(":")
	dsn.WriteString(i.config.Password)
	dsn.WriteString("@")
	dsn.WriteString(i.config.Host)
	dsn.WriteString(":")
	dsn.WriteString(fmt.Sprintf("%d", i.config.Port))
	dsn.WriteString("/")
	dsn.WriteString(i.config.Database)
	dsn.WriteString("?")

	if i.config.ReadTimeout > 0 {
		dsn.WriteString("read_timeout=")
		dsn.WriteString(fmt.Sprintf("%d", i.config.ReadTimeout))
		dsn.WriteString("s")
	}
	if i.config.DialTimeout > 0 {
		dsn.WriteString("&dial_timeout=")
		dsn.WriteString(fmt.Sprintf("%d", i.config.DialTimeout))
		dsn.WriteString("s")
	}

	conn := clickhouse.Open(dsn.String())
	i.db, err = gorm.Open(conn, &gorm.Config{
		Logger: &logger.GormLogger{
			Config: gormlogger.Config{
				SlowThreshold:             200 * time.Millisecond,
				LogLevel:                  gormlogger.Warn,
				IgnoreRecordNotFoundError: false,
			},
		},
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true, // 使用单数表名
		},
	})
	if err != nil {
		return err
	}

	cLog.WithContext(nil, map[string]any{
		"source":     "cDB.Connector.Connect",
		"connection": i.connection,
	}, []cLog.Option{
		cLog.IncludePaths{},
	}).Trace("建立 clickhouse 连接客户端 success")

	if i.config.Pool == nil {
		return err
	}

	sqlDB, err := i.db.DB()
	if err != nil {
		return err
	}

	sqlDB.SetMaxIdleConns(i.config.Pool.MaxIdleConnections)
	sqlDB.SetMaxOpenConns(i.config.Pool.MaxConnections)
	sqlDB.SetConnMaxLifetime(time.Duration(i.config.Pool.ConnectTimeout) * time.Second)
	sqlDB.SetConnMaxIdleTime(time.Duration(i.config.Pool.MaxIdleTime) * time.Second)

	return err
}

func NewClickhouseConnector(connection string, config *Connection) (*Connector, error) {
	connector := &Connector{
		connection: connection,
		config:     config,
		db:         nil,
	}
	err := connector.Connect()

	return connector, err
}
